package com.msh.service.impl;

import com.alibaba.excel.event.Order;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Strings;
import com.msh.common.VMSystem;
import com.msh.entity.OrderCollectEntity;
import com.msh.entity.OrderEntity;
import com.msh.http.viewModel.SalesInfo;
import com.msh.http.viewModel.SkuCollect;
import com.msh.service.OrderCollectService;
import com.msh.service.OrderService;
import com.msh.service.ReportService;
import com.msh.viewmodel.BarCharCollect;
import com.msh.viewmodel.Pager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.similarities.Lambda;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author :zhangchuanfu
 * @Date :2023/2/3
 * @Time :14:16
 * @description :合作商分成总汇报表
 *
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ReportServiceImpl implements ReportService {

    private final OrderCollectService orderCollectService;

    private final RestHighLevelClient esclient;

    private final OrderService orderService;

    @Override
    public Pager<OrderCollectEntity> getPartnerCollect(Long pageIndex, Long pageSize, String name, LocalDate start, LocalDate end) {
        Page<OrderCollectEntity> page = new Page<>(pageIndex, pageSize);
        var qw = new QueryWrapper<OrderCollectEntity>();

        qw.select("IFNULL(sum(order_count),0) as order_count",
                "IFNULL(sum(total_bill),0) as order_total_money",
                "IFNULL(min(ratio),0) as ratio",
                "IFNULL(sum(total_bill),0) as totalBill",
                "owner_name","date");
        if (!Strings.isNullOrEmpty(name)) {
            qw.lambda().like(OrderCollectEntity::getOwnerName,name);
        }
        qw.lambda()
                .ge(OrderCollectEntity::getDate,start)
                .le(OrderCollectEntity::getDate,end)
                .groupBy(OrderCollectEntity::getOwnerName,OrderCollectEntity::getDate)
                .orderByDesc(OrderCollectEntity::getDate);

        return Pager.build(orderCollectService.page(page,qw));
    }

    @Override
    public List<OrderCollectEntity> getTop12(Integer partnerId) {
        LambdaQueryWrapper<OrderCollectEntity> qw = new LambdaQueryWrapper<>();
        qw.select(OrderCollectEntity::getDate,OrderCollectEntity::getNodeName,OrderCollectEntity::getOrderCount,OrderCollectEntity::getTotalBill)
                .eq(OrderCollectEntity::getOwnerId,partnerId)
                .orderByDesc(OrderCollectEntity::getDate)
                .last("limit 12");

        return orderCollectService.list(qw);
    }

    @Override
    public Pager<OrderCollectEntity> search(Long pageIndex, Long pageSize, Integer partnerId,String nodeName, LocalDate start, LocalDate end) {
        Page<OrderCollectEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<OrderCollectEntity> qw = new LambdaQueryWrapper<>();
        qw.select(OrderCollectEntity::getDate,OrderCollectEntity::getNodeName,OrderCollectEntity::getOrderCount,OrderCollectEntity::getTotalBill)
                .eq(OrderCollectEntity::getOwnerId,partnerId)
                .orderByDesc(OrderCollectEntity::getDate);
        if (!Strings.isNullOrEmpty(nodeName)) {
            qw.like(OrderCollectEntity::getNodeName, nodeName);
        }
        if (start != null && end != null) {
            qw.ge(OrderCollectEntity::getDate,start).le(OrderCollectEntity::getDate,end);
        }
        return Pager.build(orderCollectService.page(page,qw));
    }

    @Override
    public List<OrderCollectEntity> getList(Integer partnerId, String nodeName, LocalDate start, LocalDate end) {
        LambdaQueryWrapper<OrderCollectEntity> qw = new LambdaQueryWrapper<>();
        qw.select(OrderCollectEntity::getDate,OrderCollectEntity::getNodeName,OrderCollectEntity::getOrderCount,OrderCollectEntity::getTotalBill)
                .eq(OrderCollectEntity::getOwnerId,partnerId)
                .orderByDesc(OrderCollectEntity::getDate);
        if (!Strings.isNullOrEmpty(nodeName)) {
            qw.like(OrderCollectEntity::getNodeName, nodeName);
        }
        if (start != null && end != null) {
            qw.ge(OrderCollectEntity::getDate,start).le(OrderCollectEntity::getDate,end);
        }
        return orderCollectService.list(qw);
    }

    @Override
    public BarCharCollect getCollect(Integer partnerId, LocalDate start, LocalDate end) {
        var qw = new QueryWrapper<OrderCollectEntity>();
        qw.select("IFNULL(sum(total_bill),0) as total_bill", "date")
                .lambda()
                .ge(OrderCollectEntity::getDate, start)
                .le(OrderCollectEntity::getDate,end)
                .eq(OrderCollectEntity::getOwnerId,partnerId)
                .groupBy(OrderCollectEntity::getDate);
        Map<LocalDate, Integer> mapCollect = orderCollectService.list(qw).stream()
                .collect(Collectors.toMap(OrderCollectEntity::getDate, OrderCollectEntity::getTotalBill));
        BarCharCollect result = new BarCharCollect();
        start.datesUntil(end.plusDays(1), Period.ofDays(1))
                .forEach(date->{
                    result.getXAxis().add(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
                    result.getSeries().add(mapCollect.getOrDefault(date, 0));
                });
        return result;
    }

    @Override
    public BarCharCollect getAmountCollect(Integer collectType, LocalDate start, LocalDate end) {
        String groupColumn = collectType == 2?"MONTH(date)":"date";

        String dateColumn = collectType == 2?"min(date) as date":"date";

        DateTimeFormatter formatter = collectType == 2?DateTimeFormatter.ofPattern("yyyy-MM"):DateTimeFormatter.ISO_LOCAL_DATE;

        Period period = collectType == 2?Period.ofMonths(1):Period.ofDays(1);

        QueryWrapper<OrderCollectEntity> qw = new QueryWrapper<>();
        qw.select("IFNULL(sum(order_total_money),0) as order_total_money",dateColumn)
                .groupBy(groupColumn)
                .lambda()
                .ge(OrderCollectEntity::getDate,start)
                .le(OrderCollectEntity::getDate,end)
                .orderByAsc(OrderCollectEntity::getDate);
        Map<String, Integer> mapCollect = orderCollectService.list(qw).stream().collect(Collectors.toMap(o -> o.getDate().format(formatter), OrderCollectEntity::getOrderTotalMoney));
        BarCharCollect result = new BarCharCollect();
        start.datesUntil(end.plusDays(1),period)
                .forEach(date -> {
                    String key = date.format(formatter);
                    result.getXAxis().add(key);
                    result.getSeries().add(mapCollect.getOrDefault(key,0));
                });
        return result;
    }

    @Override
    public BarCharCollect getCollectByRegion(LocalDate start, LocalDate end) {
        SearchRequest searchRequest= new SearchRequest("order");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        boolQueryBuilder.filter(QueryBuilders.rangeQuery("create_time").lte(end).gte(start));
        boolQueryBuilder.filter(QueryBuilders.termQuery("pay_status", VMSystem.PAY_STATUS_PAYED));
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.size(0);//不需要查询具体的数据

        AggregationBuilder regionAgg = AggregationBuilders.terms("region")
                .field("region_name")
                .subAggregation(AggregationBuilders.sum("amount_sum").field("amount"))
                .order(BucketOrder.aggregation("amount_sum",false))
                .size(30);
        sourceBuilder.aggregation(regionAgg);
        searchRequest.source(sourceBuilder);

        BarCharCollect result = new BarCharCollect();

        try {
            SearchResponse response = esclient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = response.getAggregations();
            if (aggregations == null) return result;
            Terms region = aggregations.get("region");
            List<? extends Terms.Bucket> buckets = region.getBuckets();
            if (buckets.size() <= 0) return result;
            buckets.forEach(b->{
                result.getXAxis().add(b.getKeyAsString());
                ParsedSum amount_sumAgg = b.getAggregations().get("amount_sum");
                double value = amount_sumAgg.getValue();
                result.getSeries().add((int) value);
            });
        } catch (IOException e) {
            log.error("根据区域汇总数据出错",e);
        }
        return result;
    }

    @Override
    public List<SkuCollect> getSkuTop(Integer top, LocalDate start, LocalDate end) {
        SearchRequest request = new SearchRequest();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        boolQueryBuilder.filter(QueryBuilders.termQuery("pay_status",VMSystem.PAY_STATUS_PAYED));
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("update_time").gte(start).lte(end));
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.size(0);//不需要查询具体的数据

        TermsAggregationBuilder orderAgg = AggregationBuilders.terms("sku").field("sku_name")
                .subAggregation(AggregationBuilders.count("count").field("sku_id"))
                .order(BucketOrder.aggregation("count", false))
                .size(top);
        sourceBuilder.query(boolQueryBuilder).aggregation(orderAgg);
        request.source(sourceBuilder);

        try {
            SearchResponse searchResponse = esclient.search(request, RequestOptions.DEFAULT);
            Aggregations aggregations = searchResponse.getAggregations();
            if (aggregations == null) return new ArrayList<>();
            Terms terms = aggregations.get("sku");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            return buckets.stream().map(o->{
                SkuCollect skuCollect = new SkuCollect();
                skuCollect.setSkuName(o.getKeyAsString());
                skuCollect.setCount((int) o.getDocCount());
                return skuCollect;
            }).collect(Collectors.toList());
        } catch (IOException e) {
            log.error("获取前{}热门商品失败",top);
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    @Override
    public int getOrderCount(Integer partnerId,Integer innerCode,LocalDateTime start, LocalDateTime end) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(partnerId != null, OrderEntity::getOwnerId, partnerId)
                .eq(innerCode!=null,OrderEntity::getInnerCode,innerCode)
                .eq(OrderEntity::getStatus,VMSystem.ORDER_STATUS_VENDOUT_SUCCESS)
                .ge(OrderEntity::getUpdateTime,start)
                .le(OrderEntity::getUpdateTime,end);
        return  orderService.count(qw);
    }

    @Override
    public long getOrderAmount(Integer partnerId,Integer innerCode,LocalDateTime start, LocalDateTime end) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.select("IFNULL(SUM(amount),0) as amount")
           .lambda()
                .eq(partnerId != null, OrderEntity::getOwnerId, partnerId)
                .eq(innerCode!=null,OrderEntity::getInnerCode,innerCode)
                .eq(OrderEntity::getStatus,VMSystem.ORDER_STATUS_VENDOUT_SUCCESS)
                .ge(OrderEntity::getUpdateTime,start)
                .le(OrderEntity::getUpdateTime,end);
        List<OrderEntity> list = orderService.list(qw);
        if (list.size() <=0 ) return 0L;
        return list.stream().mapToLong(OrderEntity::getAmount).sum();
//        BigDecimal divide = BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(100));
//        return divide.toString();
    }

    @Override
    public List<SkuCollect> getSkuCollect(Integer innerCode, LocalDate start, LocalDate end) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.select("sku_name as skuName","COUNT(*) as amount")
                .lambda()
                .eq(OrderEntity::getInnerCode,innerCode)
                .ge(OrderEntity::getUpdateTime,start)
                .le(OrderEntity::getUpdateTime,end)
                .groupBy(OrderEntity::getSkuName);
        List<OrderEntity> list = orderService.list(qw);
        if (list.isEmpty()) return new ArrayList<>();
        return list.stream().map(o->{
            SkuCollect skuCollect = new SkuCollect();
            skuCollect.setSkuName(o.getSkuName());
            skuCollect.setCount(o.getAmount());
            return skuCollect;
        }).collect(Collectors.toList());
    }

    @Override
    public SalesInfo getAmountInfo(Integer partnerId, LocalDateTime start, LocalDateTime end) {
        //构建返回值
        SalesInfo salesInfo = new SalesInfo();

        //获取合作商当日收入数据
        LocalDateTime today_start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);

        QueryWrapper<OrderEntity> dayQW = new QueryWrapper<>();
        dayQW.select("IFNULL(SUM(bill),0) as bill")
                .lambda()
                .eq(OrderEntity::getOwnerId,partnerId)
                .ge(OrderEntity::getUpdateTime,today_start)
                .le(OrderEntity::getUpdateTime,LocalDateTime.now());
        OrderEntity day = orderService.getOne(dayQW);
        salesInfo.setDayMoney(Long.valueOf(day.getBill()));//设置当日收入

        //获取合作商本周收入数据
        QueryWrapper<OrderCollectEntity> qw = new QueryWrapper<>();
        qw
                .select("IFNULL(SUM(total_bill),0) as total_bill")
                .lambda()
                .eq(OrderCollectEntity::getOwnerId,partnerId)
                .ge(OrderCollectEntity::getDate,start)
                .le(OrderCollectEntity::getDate,end);
        OrderCollectEntity orderCollect = orderCollectService.getOne(qw);
        //获取合作商上周收入数据
        qw
                .lambda()
                .ge(OrderCollectEntity::getDate,start.plusDays(-7))//上周起点
                .le(OrderCollectEntity::getDate,start);
        OrderCollectEntity beforeOrderCollect = orderCollectService.getOne(qw);

        //属性填充
        salesInfo.setCurrentAmount((long) orderCollect.getTotalBill());
        int percent;
        if (beforeOrderCollect.getTotalBill() == 0 && orderCollect.getTotalBill() == 0 ) percent = 0;
        else if (beforeOrderCollect.getTotalBill() == 0) percent = 100;
        else percent = orderCollect.getTotalBill() == beforeOrderCollect.getTotalBill() ? 0 :
                (orderCollect.getTotalBill() - beforeOrderCollect.getTotalBill()) / beforeOrderCollect.getTotalBill() * 100;
        salesInfo.setPercent(percent);
        return salesInfo;
    }

    @Override
    public SalesInfo getOrderCountInfo(Integer partnerId, LocalDateTime start, LocalDateTime end) {
        SalesInfo salesInfo = new SalesInfo();//构建返回值

        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        //获取当天订单数
        LocalDateTime dayStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);//当天最小时间
        qw.select("count(*) as amount")
                .lambda()
                .eq(OrderEntity::getOwnerId,partnerId)
                .ge(OrderEntity::getUpdateTime,dayStartTime)
                .le(OrderEntity::getUpdateTime,LocalDateTime.now());
        OrderEntity day = orderService.getOne(qw);
        salesInfo.setDayOrderNumber(day.getAmount());
        //获取本周订单数
        qw.lambda()
                .ge(OrderEntity::getUpdateTime,start)
                .le(OrderEntity::getUpdateTime,LocalDateTime.now());
        OrderEntity thisWeek = orderService.getOne(qw);

        //获取上周订单数
        qw.lambda().ge(OrderEntity::getUpdateTime,start.plusDays(-7))
                .le(OrderEntity::getUpdateTime,start);
        OrderEntity beforeWeek = orderService.getOne(qw);

        //构建返回值
        int percent;
        if (thisWeek.getAmount() == 0 && beforeWeek.getAmount() == 0) percent = 0;
        else if(beforeWeek.getAmount() == 0) percent = 100;
        else percent = Objects.equals(thisWeek.getAmount(), beforeWeek.getAmount()) ? 0:(thisWeek.getAmount() - beforeWeek.getAmount())
                    / beforeWeek.getAmount() * 100;

        salesInfo.setCurrentAmount(Long.valueOf(thisWeek.getAmount()));
        salesInfo.setPercent(percent);
        return salesInfo;
    }

    @Override
    public long getTotalBill(LocalDateTime start, LocalDateTime end) {
        return orderCollectService.getTotalBill(start,end);
    }
}
